package config

import (
	"encoding/json"
	"fmt"
	"github.com/rs/zerolog/log"
	"gorm.io/gorm"
	"os"
	"path/filepath"
	"shyxy-model-agent/common/cache/lru"
	"shyxy-model-agent/core/repository"
)

type DBLoader struct {
	config                     MysqlConfig
	configPath                 string
	db                         *gorm.DB
	modelInfoRepo              repository.ModelInfoRepository
	modelContainerInstanceRepo repository.ModelContainerInstanceRepository
	grpcInstanceRepo           repository.GrpcInstanceRepository
	selfBuildModelRepo         repository.SelfBuildModelRepository
	onlineModelRepo            repository.OnlineModelRepository
}

func NewDBLoader(configPath string) *DBLoader {
	return &DBLoader{
		configPath: configPath,
	}
}

func readMysqlConfigFromFile(file string) (*MysqlConfig, error) {
	mc := &MysqlConfig{}
	f, err := os.ReadFile(file)
	if err != nil {
		return nil, fmt.Errorf("readMysqlConfigFromFile cannot read config file %q: %w", file, err)
	}
	if err := json.Unmarshal(f, mc); err != nil {
		return nil, fmt.Errorf("readMysqlConfigFromFile cannot unmarshal config file %q: %w", file, err)
	}
	log.Debug().Msgf("db_config_loader.readMysqlConfig:mysql config: %+v", mc)
	return mc, nil
}

func (dbl *DBLoader) LoadMysqlConfig(file string) error {
	filePath := filepath.Join(dbl.configPath, file)
	c, err := readMysqlConfigFromFile(filePath)
	if err != nil {
		return fmt.Errorf("LoadMysqlConfig cannot read config file %q: %w", file, err)
	}
	dbl.config = *c
	return nil
}

func (dbl *DBLoader) DB() (*gorm.DB, error) {
	if dbl.db != nil {
		return dbl.db, nil
	}
	db, err := dbl.config.DB()
	if err != nil {
		return nil, fmt.Errorf("DB cannot connect to database: %w", err)
	}
	dbl.db = db
	return db, nil
}

func (dbl *DBLoader) ModelInfoRepo() repository.ModelInfoRepository {
	if dbl.modelInfoRepo != nil {
		return dbl.modelInfoRepo
	}
	db, err := dbl.DB()
	if err != nil {
		panic(fmt.Errorf("ModelInfoRepo cannot connect to database: %w", err))
	}
	repo := repository.NewModelInfoRepositoryWithCache(db, lru.New(1000))
	dbl.modelInfoRepo = repo
	return repo
}

func (dbl *DBLoader) ModelContainerInstanceRepo() repository.ModelContainerInstanceRepository {
	if dbl.modelContainerInstanceRepo != nil {
		return dbl.modelContainerInstanceRepo
	}
	db, err := dbl.DB()
	if err != nil {
		panic(fmt.Errorf("ModelContainerInstanceRepo cannot connect to database: %w", err))
	}
	repo := repository.NewModelContainerInstanceRepositoryWithCache(db, lru.New(1000))
	dbl.modelContainerInstanceRepo = repo
	return repo
}

func (dbl *DBLoader) GrpcInstanceRepo() repository.GrpcInstanceRepository {
	if dbl.grpcInstanceRepo != nil {
		return dbl.grpcInstanceRepo
	}
	db, err := dbl.DB()
	if err != nil {
		panic(fmt.Errorf("GrpcInstanceRepo cannot connect to database: %w", err))
	}
	repo := repository.NewGrpcInstanceRepositoryWithCache(db, lru.New(1000))
	dbl.grpcInstanceRepo = repo
	return repo
}

func (dbl *DBLoader) SelfBuildModelRepo() repository.SelfBuildModelRepository {
	if dbl.selfBuildModelRepo != nil {
		return dbl.selfBuildModelRepo
	}
	db, err := dbl.DB()
	if err != nil {
		panic(fmt.Errorf("SelfBuildModelRepo cannot connect to database: %w", err))
	}
	repo := repository.NewSelfBuildModelRepositoryWithCache(db, nil)
	dbl.selfBuildModelRepo = repo
	return repo
}

func (dbl *DBLoader) OnlineModelRepo() repository.OnlineModelRepository {
	if dbl.onlineModelRepo != nil {
		return dbl.onlineModelRepo
	}
	db, err := dbl.DB()
	if err != nil {
		panic(fmt.Errorf("OnlineModelRepo cannot connect to database: %w", err))
	}
	repo := repository.NewOnlineModelRepositoryWithCache(db, nil)
	dbl.onlineModelRepo = repo
	return repo
}
